package org.jetlinks.community.mstn.web;


import io.scalecube.services.exceptions.BadRequestException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.annotation.QueryAction;
import org.hswebframework.web.authorization.annotation.Resource;
import org.hswebframework.web.crud.service.ReactiveCrudService;
import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
import org.jetlinks.community.auth.enums.OrgType;
import org.jetlinks.community.dashboard.web.request.DashboardMeasurementRequest;
import org.jetlinks.community.device.entity.DeviceInstanceEntity;
import org.jetlinks.community.device.entity.DeviceProperty;
import org.jetlinks.community.device.service.LocalDeviceInstanceService;
import org.jetlinks.community.mstn.MstnBaseController;
import org.jetlinks.community.mstn.entity.DeviceAliasEntity;
import org.jetlinks.community.mstn.entity.vo.*;
import org.jetlinks.community.mstn.service.DeviceAliasService;
import org.jetlinks.community.mstn.service.MstnSystemService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数字孪生
 *
 */
@RestController
@RequestMapping("/alias")
@Authorize
@Resource(id = "alias", name = "数字孪生", group = "mstn")
@Tag(name = "数字孪生")
@AllArgsConstructor
public class DeviceAliasController extends MstnBaseController implements ReactiveServiceCrudController<DeviceAliasEntity, String> {
    private final DeviceAliasService deviceAliasService;

    private final MstnSystemService mstnSystemService;

    private final LocalDeviceInstanceService deviceInstanceService;
    @Override
    public ReactiveCrudService<DeviceAliasEntity, String> getService() {
        return deviceAliasService;
    }

    /**
     * 查询数字孪生
     */
    @PostMapping("/list")
    @Operation(summary = "获取数字孪生列表")
    public Mono<PagerResult<DeviceAliasEntity>> list(@RequestBody Mono<QueryParamEntity> query) {
        return query.flatMap(deviceAliasService::queryDeviceAlias);
    }

    /**
     * 查询项目下设备列表
     */
    @PostMapping("/_query/project/device")
    @Operation(summary = "查询项目下设备列表")
    public Mono<PagerResult<DeviceInstanceEntity>> queryAllDevice(@RequestBody Mono<QueryParamEntity> query) {
        return getAssets(OrgType.project.getValue(), false ,query)
                .flatMap(deviceInstanceService::queryPager);
    }
    /**
     * 查询项目下设备列表
     */
    @PostMapping("/_query/project/device/no-paging")
    @Operation(summary = "查询项目下设备列表【不分页】")
    public Mono<List<DeviceInstanceEntity>> queryAllDeviceWithNoPage(@RequestBody Mono<QueryParamEntity> query) {
        return getAssets(OrgType.project.getValue(), false ,query)
            .flatMap(param ->
            {
                param.setPaging(false);
                return deviceInstanceService.query(param).collectList();
            });
    }
    @PostMapping("/_query/device")
    @QueryAction
    @Operation(summary = "查询项目下终端设备列表")
    public Mono<PagerResult<DeviceInstanceEntity>> queryDevice(@RequestBody Mono<QueryParamEntity> query) {
        return getAssets(OrgType.project.getValue(), false , query)
                .flatMap(queryParamEntity ->
                    deviceAliasService.getDeviceList(queryParamEntity)
                    .flatMap(pagerResult -> {
                        if (pagerResult.getTotal() == 0) {
                            return Mono.just(PagerResult.empty());
                        }
                        //有网关的子设备
                        Map<String, DeviceInstanceEntity> mapping =
                            pagerResult.getData()
                                .stream()
                                .filter(entity -> entity.getParentId()!= null)
                                .collect(Collectors.toMap(DeviceInstanceEntity::getParentId, Function.identity()));
                        if (mapping.isEmpty()) {
                            return Mono.just(pagerResult);
                        }
                        return deviceInstanceService.createQuery().in(DeviceInstanceEntity::getId, mapping.keySet())
                           .fetch()
                           .collect(Collectors.toMap(DeviceInstanceEntity::getId, DeviceInstanceEntity::getName))
                           .flatMap(
                               gatewayNameMapping -> {
                                   pagerResult.getData()
                                      .stream()
                                      .filter(entity -> entity.getParentId()!= null)
                                      .forEach(entity -> {
                                           entity.setParentName(gatewayNameMapping.get(entity.getParentId()));
                                       });
                                   return Mono.just(pagerResult);
                                }
                           );
                    })
            );
    }

    @PostMapping("/_query/gateway")
    @QueryAction
    @Operation(summary = "查询项目下网关设备列表")
    public Mono<PagerResult<GatewayListResVO>> queryGatewayDevice(@RequestBody Mono<QueryParamEntity> query) {
        return  getAssets(OrgType.project.getValue(), false , query)
                    .flatMap(
                        queryParamEntity -> deviceAliasService
                            .getGatewayProductList(queryParamEntity)
                            .flatMap(pagerResult -> {
                                if (pagerResult.getTotal() == 0) {
                                    return Mono.just(PagerResult.empty());
                                }
                                Map<String, DeviceInstanceEntity> mapping =
                                    pagerResult.getData()
                                        .stream()
                                        .collect(Collectors.toMap(DeviceInstanceEntity::getId, Function.identity()));
                                //查询所有子设备并按父设备ID分组
                                return deviceInstanceService
                                            .createQuery()
                                            .where()
                                            .in(DeviceInstanceEntity::getParentId, mapping.keySet())
                                            .fetch()
                                            .groupBy(DeviceInstanceEntity::getParentId, Integer.MAX_VALUE)
                                            .flatMap(group -> {
                                                String parentId = group.key();
                                                return group
                                                    .collectList()
                                                    //将父设备和分组的子设备合并在一起
                                                    .map(children -> GatewayListResVO.of(mapping.get(parentId), children.size()));
                                            })
                                            .collectList().flatMap(list -> Mono.just(PagerResult.of(pagerResult.getTotal(), list, queryParamEntity)));
                    }));
    }

    @PutMapping("/_bind/{siteId}/device")
    @Operation(summary = "绑定设备到站点 【一个设备对一个站点】")
    public Mono<Integer> bindDeviceToSite(@PathVariable String siteId,@RequestBody List<String > deviceIds) {
        return deviceInstanceService.bindDevicesToOrg(siteId, deviceIds);
    }

    @PutMapping("/_unbind/{siteId}/device")
    @Operation(summary = "从站点解绑设备 【一个设备对一个站点】")
    public Mono<Integer> unbindDeviceToSite(@PathVariable String siteId,@RequestBody List<String > deviceIds) {
        return deviceInstanceService.unbindDevicesFromOrg(siteId, deviceIds);
    }

    /**
     * 获取设备数据
     */
    @PostMapping("/_query/device/value")
    @Operation(summary = "查询设备的值")
    public Mono<List<DeviceValueResVO>> getDeviceValue(@RequestBody Flux<DeviceValueReqVO> requests) {

        Flux<DashboardMeasurementRequest> request = requests.map(req ->
            mstnSystemService.initRequest(req.getDeviceId(), req.getProductId(), req.getProperty()));
        return mstnSystemService.getDeviceProperty(request)
            .collectList()
            .flatMap(list -> {
                Map<String, DeviceValueResVO> map = new HashMap<>();
                for (DeviceProperty property : list) {
                    if (map.containsKey(property.getDeviceId())){
                        map.get(property.getDeviceId()).add(property);
                    }else {
                        map.put(property.getDeviceId(), DeviceValueResVO.of(property));
                    }
                }
                List<DeviceValueResVO> values = new ArrayList<>(map.values());
                return Mono.just(values);
            })
            ;
    }


    @PostMapping("/_query/{productId}/{value}")
    @Operation(summary = "按产品统计属性值")
    public Mono<Map<Object, Long>> queryByProduct(@PathVariable String productId, @PathVariable String value, @RequestBody List<ProductAggReqVO> aggList){
        Flux<DashboardMeasurementRequest> request = deviceInstanceService
                .createQuery()
                .where(DeviceInstanceEntity::getProductId, productId)
                .fetch()
                .map(device -> mstnSystemService.initRequest(device.getId(), device.getProductId(), new ArrayList<>((Collections.singleton(value)))));
        return mstnSystemService.getDeviceProperty(request)
                .collectList()
                .map(list ->
                    list
                        .stream()
                        .map(property -> ProductAggResVO.of(aggList, property))
                        .collect(Collectors.groupingBy(ProductAggResVO::getName,Collectors.counting())));
    }
}
